"""
该模块用于自动下载Refworks文件。

主要功能包括：
1. 初始化WebDriver。
2. 打开搜索页面并执行搜索。
3. 导出搜索结果并处理下载。

模块中的主要类和函数：
- WebDriverHandler: 用于处理WebDriver操作的类。
  - __init__: 初始化WebDriver和选项。
  - open_search_page: 打开搜索页面并点击期刊搜索和专业搜索选择器。
  - wait_for_element: 等待页面上的元素出现。
  - switch_to_new_window: 切换WebDriver的焦点到新窗口。
  - execute_search: 在网页上执行搜索。
  - wait_for_elements: 等待页面上所有匹配选择器的元素出现。
  - execute_export: 导出选中的文章并清除选择。
  - hover_and_click: 悬停在元素上并点击。
  - wait_and_click: 等待元素出现并点击。
  - journal_level: 点击对应期刊级别的复选框。
  - close_specific_window: 通过句柄关闭特定窗口。
  - click_blank_area: 点击网页上的空白区域。
"""
# TODO：暂未解决第二次导出时，无法点击导出按钮，导致程序无法继续执行的问题

import logging
import os
import time
from selenium import webdriver
from selenium.common import (
    TimeoutException,
    NoSuchElementException, NoSuchWindowException,
)
from selenium.webdriver import ActionChains
from selenium.webdriver.edge.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

from main.config import WEB_DRIVER_PATH, START_URL, JOURNAL_SEARCH_SELECTOR, \
    MAJOR_SEARCH_SELECTOR, DELAY, CHECKBOX, TEXT_AREA_SELECTOR, \
    START_SEARCH_BUTTON_SELECTOR, RESULT_TABLE_LIST_SELECTOR, \
    JOURNAL_QUALITY_SELECTOR, START_YEAR_SELECTOR, END_YEAR_SELECTOR, \
    START_YEAR, END_YEAR

class WebDriverHandler:
    """
    用于处理WebDriver操作的类。

    属性:
        options (Options): WebDriver选项。
        driver (WebDriver): WebDriver实例。

    方法:
        open_search_page: 打开搜索页面并点击期刊搜索和专业搜索选择器。
        wait_for_element: 等待页面上的元素出现。
        switch_to_new_window: 切换WebDriver的焦点到新窗口。
        execute_search: 在网页上执行搜索。
        wait_for_elements: 等待页面上所有匹配选择器的元素出现。
        export_and_clear: 导出选中的文章并清除选择。
        hover_and_click: 悬停在元素上并点击。
        wait_and_click: 等待元素出现并点击。
        journal_level: 点击对应期刊级别的复选框。
        close_specific_window: 通过句柄关闭特定窗口。
        click_blank_area: 点击网页上的空白区域。
    """

    def __init__(self, journal_name):
        """
        初始化WebDriver和选项。
        """
        self.options = Options()
        # self.options.add_argument("--headless")
        base_directory = r"S:\PythonProject\cnki-references-scrapy\auto_download_refworks_file"
        self.default_directory = os.path.join(base_directory, journal_name)
        self.options.add_experimental_option(
            "prefs",
            {
                "download.default_directory": self.default_directory,
                "download.prompt_for_download": False,
            },
        )
        self.driver = webdriver.Edge(
            service=webdriver.EdgeService(executable_path=WEB_DRIVER_PATH),
            options=self.options,
        )
        logging.basicConfig(level=logging.INFO)

    def open_search_page(self):
        """
        打开搜索页面并点击期刊搜索和专业搜索选择器。
        """
        logging.info(
            "=============Start Download Refworks File========================"
        )
        self.driver.get(START_URL)
        self.wait_and_click(JOURNAL_SEARCH_SELECTOR)
        self.wait_and_click(MAJOR_SEARCH_SELECTOR)
        self.wait_for_element(START_YEAR_SELECTOR).send_keys(START_YEAR)
        self.wait_for_element(END_YEAR_SELECTOR).send_keys(END_YEAR)

    def wait_for_element(self, selector):
        """
        等待页面上的元素出现。

        参数:
            selector (str): 元素的CSS选择器。

        返回值:
            WebElement: 第一个匹配的WebElement。
        """
        try:
            return WebDriverWait(self.driver, DELAY).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, selector))
            )
        except TimeoutException:
            logging.error(f"Element is Timeout: {selector}, restart...")
            return None

    def switch_to_new_window(self, current_handle, driver):
        """
        切换WebDriver的焦点到新窗口。

        参数:
            current_handle (str): 当前窗口的句柄。
            driver (WebDriver): 用于与网页交互的WebDriver实例。

        返回值:
            str: 新窗口的句柄，如果找到则返回，否则返回None。
        """
        all_windows = driver.window_handles
        try:
            for window in all_windows:
                if window != current_handle:
                    driver.switch_to.window(window)
                    return window
        except NoSuchElementException:
            logging.warning("No new window found.")

    def execute_search(self, search_query):
        """
        在网页上执行搜索。

        参数:
            search_query (str): 要执行的搜索查询。
        """
        _EXPORT_UPPER_LIMIT = 20
        logging.info("=============Start Search========================")
        self.open_search_page()
        # self.journal_level(CHECKBOX)

        self.wait_for_element(TEXT_AREA_SELECTOR).send_keys(search_query)
        self.wait_and_click(START_SEARCH_BUTTON_SELECTOR)
        logging.info("=============Get Total Page Number========================")

        self.page_total_number = int(
            self.wait_for_element("[class='countPageMark']").get_attribute(
                "data-pagenum"
            )
        )
        selected_articles = 0

        for i in range(1, self.page_total_number + 1):
            # 在执行全选操作时，需要等待页面加载完成，才进行全选操作，否则会出现遗漏内容
            try:
                WebDriverWait(self.driver, DELAY).until(
                    EC.presence_of_all_elements_located((By.CSS_SELECTOR, RESULT_TABLE_LIST_SELECTOR))
                )
                logging.info(f"第 {i} 页加载完成")
            except TimeoutException:
                logging.warning(f"第 {i} 页加载超时，可能会遗漏部分内容")
            
            # 确保页面完全稳定
            time.sleep(2)            
            self.wait_and_click("#selectCheckAll1")
            time.sleep(2)
            articles_on_page = len(self.wait_for_elements(RESULT_TABLE_LIST_SELECTOR))
            selected_articles += articles_on_page
            # time.sleep(DELAY)

            if i == self.page_total_number:
                logging.info("执行导出操作中")
                self.execute_export()
                logging.info("导出成功")
                self.driver.quit()
                break

            if selected_articles >= _EXPORT_UPPER_LIMIT:
                logging.info("执行导出操作中")
                self.execute_export()
                logging.info("导出成功")
                selected_articles = 0

            self.wait_and_click("#Page_next_top")

    def execute_export(self):
        """
        执行导出操作。
        """
        WebDriverWait(self.driver, DELAY).until(
            EC.presence_of_element_located((By.CSS_SELECTOR,
                                            "#batchOpsBox > li:nth-child(2) > ul > li.export > ul > li:nth-child(8) > a"))
        )
        self.export_operation()
        # self.click_blank_area()
        self.clear_operation()

    def wait_for_elements(self, selector):
        """
        等待页面上所有匹配选择器的元素出现。

        参数:
            selector (str): 元素的CSS选择器。

        返回值:
            list of WebElement: 定位到的WebElement列表。
        """
        try:
            elements = WebDriverWait(self.driver, DELAY).until(
                EC.presence_of_all_elements_located((By.CSS_SELECTOR, selector))
            )
            return elements
        except TimeoutException:
            logging.error(f"Element is Timeout: {selector}")
            # self.restart_driver()

    def export_operation(self):
        """
        执行导出操作。
        """
        try:
            logging.info("点击导出与分析按钮")
            self.hover_and_click("#batchOpsBox > li:nth-child(2) > a")
            WebDriverWait(self.driver, DELAY).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, "#batchOpsBox > li:nth-child(2) > ul > li.export > a"))
            )
            self.hover_and_click("#batchOpsBox > li:nth-child(2) > ul > li.export > a")
            logging.info("选择导出格式")
            export_button = self.wait_for_element(
                "#batchOpsBox > li:nth-child(2) > ul > li.export > ul > li:nth-child(8) > a"
            )

            if export_button:
                ActionChains(self.driver).move_to_element(export_button).click().perform()
                WebDriverWait(self.driver, DELAY).until(
                    EC.presence_of_element_located((By.CSS_SELECTOR, "#litotxt > a"))
                )
            else:
                logging.error("导出按钮未找到")

            logging.info("切换到文献管理界面")
            current_window_handle = self.driver.current_window_handle
            new_window_handle = self.switch_to_new_window(current_window_handle, driver=self.driver)

            execute_button = self.wait_for_element("#litotxt > a")

            # Check if the current page is the second page and pause for manual inspection
            if self.page_total_number == 2 and execute_button is None:
                logging.info("======测试第二页的异常情况，暂停界面=========")
                time.sleep(10000)  # Pause for manual inspection

            if new_window_handle in self.driver.window_handles:
                ActionChains(self.driver).move_to_element(execute_button).click().perform()

            self.driver.close()
            self.driver.switch_to.window(self.driver.window_handles[0])

        except NoSuchWindowException:
            logging.error("No such window: target window already closed")
            self.restart_driver()
        except Exception as e:
            logging.error(f"An error occurred during export: {e}")
            self.restart_driver()

    def clear_operation(self):
        """
        清除选择。
        """
        try:
            self.wait_and_click(
                "#briefBox > div:nth-child(1) > div > div.toolbar-col > div.checkcount > a"
            )
        except Exception as e:
            logging.error(f"An error occurred during clear operation: {e}")
            self.restart_driver()

    def click_blank_area(self):
        """
        点击网页上的空白区域。
        """
        # 针对Microsoft Edge浏览器，点击屏幕中心区域，从而消除下载弹窗
        # 获取浏览器窗口大小
        window_size = self.driver.get_window_size()
        viewport_width = window_size['width']
        viewport_height = window_size['height']
        # 计算屏幕中心点坐标
        center_x = viewport_width // 2
        center_y = viewport_height // 2

        # 确保坐标在视口范围内
        center_x = min(max(center_x, 0), viewport_width - 1)
        center_y = min(max(center_y, 0), viewport_height - 1)

        # 使用ActionChains移动到屏幕中心并点击
        try:
            ActionChains(self.driver).move_by_offset(center_x, center_y).click().perform()
            logging.info("已点击屏幕中心区域")
        except Exception as e:
            logging.error(f"点击屏幕中心区域时发生错误: {e}")
        
        # 重置鼠标位置
        ActionChains(self.driver).move_by_offset(-center_x, -center_y).perform()
        # blank_area = self.wait_for_element("body")
        # if blank_area:
        #     ActionChains(self.driver).move_to_element_with_offset(
        #         blank_area, 5, 5
        #     ).click().perform()
        # else:
        #     logging.error("Blank area element not found.")

    


    def restart_driver(self):
        """
        重启WebDriver。
        """
        self.driver.quit()
        self.driver = webdriver.Edge(
            service=webdriver.EdgeService(executable_path=WEB_DRIVER_PATH),
            options=self.options,
        )
        logging.info("WebDriver has been restarted.")

    def hover_and_click(self, selector):
        """
        悬停在元素上并点击。

        参数:
            selector (str): 元素的CSS选择器。
        """
        element = self.wait_for_element(selector)
        if element:
            webdriver.ActionChains(self.driver).move_to_element(
                element
            ).click().perform()

    def wait_and_click(self, selector):
        """
        等待元素出现并点击。

        参数:
            selector (str): 元素的CSS选择器。
        """
        element = self.wait_for_element(selector)
        if element:
            element.click()

    def journal_level(self, checkboxes):
        """
        点击对应期刊级别的复选框。

        参数:
            checkboxes (list): 要点击的复选框列表。
        """
        for checkbox in checkboxes:
            self.wait_and_click(f"{JOURNAL_QUALITY_SELECTOR}[key='{checkbox}']")

    def close_specific_window(self, window_handle):
        """
        通过句柄关闭特定窗口。

        参数:
            window_handle (str): 要关闭的窗口句柄。
        """
        all_windows = self.driver.window_handles
        if window_handle in all_windows:
            self.driver.switch_to.window(window_handle)
            self.driver.close()

def main():
    """主函数"""
    # "档案学通讯", "情报资料工作","情报理论与实践", "情报杂志",
    journals = [
        "图书情报工作",
        "图书情报知识", "中国图书馆学报", "档案学研究", "情报学报", "情报科学",
        "数据分析与知识发现", "大学图书馆学报", "图书与情报", "国家图书馆学刊",
        "图书馆论坛", "现代情报", "信息资源管理学报", "图书馆杂志", "图书馆建设",
        "图书馆学研究"
    ]
    for journal in journals:
        refworks_download = WebDriverHandler(journal)
        search_query = f'LY="{journal}"'
        logging.info("开始下载期刊: %s", journal)
        refworks_download.execute_search(search_query)


if __name__ == "__main__":
    main()
